///////////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
#include <Scene/Camera/Camera.h>
#include <Math/Matrix4x4.h>

//-----------------------------------------------------------------------------

Camera::Camera ()
	:	Mover ()
{
	m_right = Vec3f ( 1, 0, 0 );
	m_up = Vec3f ( 0, 1, 0 );
	m_look = Vec3f ( 0, 0, 1 );
	m_fov = 60;
	m_aspect = 4.0f/3.0f;
	m_near = 0.1f;
	m_far = 1000.0f;
	m_needProjectionRefresh = true;
	refresh ();
}

//-----------------------------------------------------------------------------

Camera::~Camera()
{
}

//-----------------------------------------------------------------------------

void Camera::Update ( f32 dt )
{
	if ( hasMoved () || m_needProjectionRefresh )
	{
		refresh ();
	}
}

//-----------------------------------------------------------------------------

void Camera::refresh ()
{
	if ( hasMoved () )
	{
		m_view.m[0][0] = m_right.x;
		m_view.m[1][0] = m_right.y;
		m_view.m[2][0] = m_right.z;

		m_view.m[0][1] = m_up.x;
		m_view.m[1][1] = m_up.y;
		m_view.m[2][1] = m_up.z;

		m_view.m[0][2] = m_look.x;
		m_view.m[1][2] = m_look.y;
		m_view.m[2][2] = m_look.z;

		m_view.m[0][3] = 0;
		m_view.m[1][3] = 0;
		m_view.m[2][3] = 0;
		m_view.m[3][3] = 1;

		m_view.m[3][0] = - getPosition().dot ( m_right );
		m_view.m[3][1] = - getPosition().dot ( m_up );
		m_view.m[3][2] = - getPosition().dot ( m_look );

		setMoved( false );
	}
	if( m_needProjectionRefresh )
	{
		m_projection.SetIdentity();
		m_projection.SetPerspective( m_fov, m_aspect, m_near, m_far );
	}
	m_wvp = m_view * m_projection;
}

//-----------------------------------------------------------------------------

f32*	Camera::getProjection()
{
	return (f32*) m_projection.m;
}

//-----------------------------------------------------------------------------

f32*	Camera::getView()
{
	return (f32*) m_view.m;
}

//-----------------------------------------------------------------------------

f32*	Camera::getWVP()
{
	return (f32*) m_wvp.m;
}

//-----------------------------------------------------------------------------

Matrix4x4	Camera::getProjectionMatrix()
{
	return m_projection;
}

//-----------------------------------------------------------------------------

Matrix4x4	Camera::getViewMatrix()
{
	return m_view;
}

//-----------------------------------------------------------------------------

Matrix4x4	Camera::getWVPMatrix()
{
	return m_wvp;
}

//-----------------------------------------------------------------------------

void	Camera::SetFov(f32 fov)
{
	ASSERT( fov > 10.f && fov < 180.f );
	m_fov = fov;
	m_needProjectionRefresh = true;
}

//-----------------------------------------------------------------------------

f32		Camera::GetFov()
{
	return m_fov;
}

//-----------------------------------------------------------------------------

void	Camera::SetAspectRatio(f32 ratio)
{
	ASSERT( ratio > 0.f );
	m_aspect = ratio;
	m_needProjectionRefresh = true;

}

//-----------------------------------------------------------------------------

f32		Camera::GetAspectRatio()
{
	return m_aspect;
}

//-----------------------------------------------------------------------------

void	Camera::SetNear(f32 dist)
{
	ASSERT( dist > 0.f );
	ASSERT( dist < m_far );
	m_near = dist;
	m_needProjectionRefresh = true;
}

//-----------------------------------------------------------------------------

f32		Camera::GetNear()
{
	return m_near;
}

//-----------------------------------------------------------------------------

void	Camera::SetFar(f32 dist)
{
	ASSERT( dist > 0.f );
	ASSERT( dist > m_near );
	m_far = dist;
	m_needProjectionRefresh = true;
}

//-----------------------------------------------------------------------------

f32		Camera::GetFar()
{
	return m_far;
}

//-----------------------------------------------------------------------------

Vec3f	Camera::GetForward()
{
	return m_look;
}

//-----------------------------------------------------------------------------

Vec3f	Camera::GetUp()
{
	return m_up;
}

//-----------------------------------------------------------------------------

Vec3f	Camera::GetRight()
{
	return m_right;
}

//-----------------------------------------------------------------------------

void	Camera::SetViewportSize(s32 width, s32 height)
{
	if( height <= 0 )
	{
		height = 1;
	}
	m_aspect = (f32) width / height;
	m_needProjectionRefresh = true;
}

//-----------------------------------------------------------------------------

void Camera::moveAlongForward ( f32 dist )
{
	moveAlongX ( dist * m_look.x );
	moveAlongY ( dist * m_look.y );
	moveAlongZ ( dist * m_look.z );
}

//-----------------------------------------------------------------------------

void Camera::moveAlongRight ( f32 dist )
{
	moveAlongX ( dist * m_right.x );
	moveAlongY ( dist * m_right.y );
	moveAlongZ ( dist * m_right.z );
}

//-----------------------------------------------------------------------------

void Camera::moveAlongUp ( f32 dist )
{
	moveAlongX ( dist * m_up.x );
	moveAlongY ( dist * m_up.y );
	moveAlongZ ( dist * m_up.z );
}

//-----------------------------------------------------------------------------

void Camera::rotateAroundForward ( f32 angle )
{
	Matrix4x4 r;
	r.SetIdentity();
	r.Rotate( angle, m_look.x, m_look.y, m_look.z );
	m_right *= r;
	m_up *= r;
	setMoved ( true );
}

//-----------------------------------------------------------------------------

void Camera::rotateAroundRight ( f32 angle )
{
	Matrix4x4 r;
	r.SetIdentity();
	r.Rotate( angle, m_right.x, m_right.y, m_right.z );
	m_look *= r;
	m_up *= r;
	setMoved ( true );
}

//-----------------------------------------------------------------------------

void Camera::rotateAroundUp ( f32 angle )
{
	Matrix4x4 r;
	r.SetIdentity();
	r.Rotate( angle, m_up.x, m_up.y, m_up.z );
	m_look *= r;
	m_right *= r;
	setMoved ( true );
}

//-----------------------------------------------------------------------------

void Camera::rotateAroundX ( f32 angle )
{
	Matrix4x4 r;
	r.SetIdentity();
	r.Rotate( angle, 1, 0, 0 );
	m_look *= r;
	m_right *= r;
	m_up *= r;
	setMoved ( true );
}

//-----------------------------------------------------------------------------

void Camera::rotateAroundY ( f32 angle )
{
	Matrix4x4 r;
	r.SetIdentity();
	r.Rotate( angle, 0, 1, 0 );
	m_look *= r;
	m_right *= r;
	m_up *= r;
	setMoved ( true );
}

//-----------------------------------------------------------------------------

void Camera::rotateAroundZ ( f32 angle )
{
	Matrix4x4 r;
	r.SetIdentity();
	r.Rotate( angle, 0, 0, 1 );
	m_look *= r;
	m_right *= r;
	m_up *= r;
	setMoved ( true );
}

//-----------------------------------------------------------------------------

//void Camera::makeUp()
//{
//	m_up.set(0,1,0);
//	m_right = *m_up.cross( &m_look ); //leak?
//	m_right.normalize();
//	m_look = *m_right.cross( &m_up ); //leak?
//	setMoved( true );
//}

//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
